/*
 * Copyright (C) 2015 tyrantgit
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tyrantgit.explosionfield;

import com.tyrantgit.explosionfield.util.AnimatorValueUtil;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.media.image.PixelMap;
import ohos.media.image.common.Position;

import java.util.Random;

public class ExplosionAnimator extends AnimatorValue implements AnimatorValue.ValueUpdateListener {

    public static final long DEFAULT_DURATION = 0x400;
    private static final float END_VALUE = 1.4f;
    private float X;
    private float Y;
    private float V;
    private float W;
    private Paint mPaint;
    private Particle[] mParticles;
    private Rect mBound;
    private ExplosionField mContainer;
    private float currentValue = 0;
    private final AnimatorValueUtil animatorValueUtil;

    public void ofFloat(float[] values) {
        animatorValueUtil.setValues(values);
        animatorValueUtil.ofFloat();
    }

    public ExplosionAnimator(ExplosionField container, PixelMap bitmap, Rect bound) {

        mPaint = new Paint();

        mBound = new Rect(bound);

        int partLen = 15;

        X = Utils.dp2Px(container.getContext(), 5);
        Y = Utils.dp2Px(container.getContext(), 20);
        V = Utils.dp2Px(container.getContext(), 2);
        W = Utils.dp2Px(container.getContext(), 1);

        mParticles = new Particle[partLen * partLen];

        Random random = new Random(System.currentTimeMillis());
        int w = bitmap.getImageInfo().size.width / (partLen + 2);
        int h = bitmap.getImageInfo().size.height / (partLen + 2);

        for (int i = 0; i < partLen; i++) {
            for (int j = 0; j < partLen; j++) {
                Position position = new Position((j + 1) * w, (i + 1) * h);
                mParticles[(i * partLen) + j] = generateParticle(bitmap.readPixel(position), random);
            }
        }

        animatorValueUtil = new AnimatorValueUtil();

        mContainer = container;
        setCurveType(CurveType.ACCELERATE_DECELERATE);
        setDuration(DEFAULT_DURATION);
        setValueUpdateListener(this::onUpdate);

    }

    private Particle generateParticle(int color, Random random) {

        Particle particle = new Particle();
        particle.color = color;
        particle.radius = V;
        if (random.nextFloat() < 0.2f) {
            particle.baseRadius = V + ((X - V) * random.nextFloat());
        } else {
            particle.baseRadius = W + ((V - W) * random.nextFloat());
        }
        float nextFloat = random.nextFloat();
        particle.top = mBound.getHeight() * ((0.18f * random.nextFloat()) + 0.2f);
        particle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat());
        particle.bottom = (mBound.getHeight() * (random.nextFloat() - 0.5f)) * 1.8f;
        float f = nextFloat < 0.2f ? particle.bottom : nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f;
        particle.bottom = f;
        particle.mag = 4.0f * particle.top / particle.bottom;
        particle.neg = (-particle.mag) / particle.bottom;
        f = mBound.getCenterX() + (Y * (random.nextFloat() - 0.5f));
        particle.baseCx = f;
        particle.cx = f;
        f = mBound.getCenterY() + (Y * (random.nextFloat() - 0.5f));
        particle.baseCy = f;
        particle.cy = f;
        particle.life = END_VALUE / 10 * random.nextFloat();
        particle.overflow = 0.4f * random.nextFloat();
        particle.alpha = 1f;
        return particle;

    }

    public boolean draw(Canvas canvas, float offsetTop) {
        if (!isRunning()) {
            return false;
        }
        for (Particle particle : mParticles) {
            particle.advance(currentValue, V);
            if (particle.alpha > 0f) {
                mPaint.setColor(new Color(particle.color));
                mPaint.setAlpha((Color.alpha(particle.color) * particle.alpha)/255);
                canvas.drawCircle(particle.cx, particle.cy - offsetTop, particle.radius, mPaint);
            }
        }
        return true;
    }

    @Override
    public void start() {
        super.start();
    }

    @Override
    public void onUpdate(AnimatorValue animatorValue, float v) {
        currentValue = animatorValueUtil.getValue(v);
        mContainer.refresh();
    }

    static class Particle {
        float alpha;
        int color;
        float cx;
        float cy;
        float radius;
        float baseCx;
        float baseCy;
        float baseRadius;
        float top;
        float bottom;
        float mag;
        float neg;
        float life;
        float overflow;

        public void advance(float factor, float V) {

            float f = 0f;
            float normalization = factor / END_VALUE;
            if (normalization < life || normalization > 1f - overflow) {
                alpha = 0f;
                return;
            }
            normalization = (normalization - life) / (1f - life - overflow);
            float f2 = normalization * END_VALUE;
            if (normalization >= 0.7f) {
                f = (normalization - 0.7f) / 0.3f;
            }
            alpha = 1f - f;
            f = bottom * f2;
            cx = baseCx + f;
            cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag;
            radius = V + (baseRadius - V) * f2;
        }
    }
}
